(function(jQuery) {

    // Make nodes selectable by expression
    jQuery.extend(jQuery.expr[':'], {
                resizable : "(' '+a.className+' ').indexOf(' ui-resizable ')"
            });

    jQuery.fn.resizable = function(o) {
        return this.each(function() {
                    if (!jQuery(this).is(".ui-resizable"))
                        new jQuery.ui.resizable(this, o);
                });
    }

    // Macros for external methods that support chaining
    var methods = "destroy,enable,disable".split(",");
    for (var i = 0; i < methods.length; i++) {
        var cur = methods[i], f;
        eval('f = function() { var a = arguments; return this.each(function() { if(jQuery(this).is(".ui-resizable")) jQuery.data(this, "ui-resizable")["'
                + cur
                + '"](a); if(jQuery(this.parentNode).is(".ui-resizable")) jQuery.data(this, "ui-resizable")["'
                + cur + '"](a); }); }');
        jQuery.fn["resizable" + cur.substr(0, 1).toUpperCase() + cur.substr(1)] = f;
    };

    // get instance method
    jQuery.fn.resizableInstance = function() {
        if (jQuery(this[0]).is(".ui-resizable")
                || jQuery(this[0].parentNode).is(".ui-resizable"))
            return jQuery.data(this[0], "ui-resizable");
        return false;
    };

    jQuery.ui.resizable = function(el, o) {

        var options = {};
        o = o || {};
        jQuery.extend(options, o); // Extend and copy options
        this.element = el;
        var self = this; // Do bindings
        jQuery.data(this.element, "ui-resizable", this);

        if (options.proxy) {
            var helper = function(e, that) {
                var helper = jQuery('<div></div>').css({
                            width : jQuery(this).width(),
                            height : jQuery(this).height(),
                            position : 'absolute',
                            left : that.options.co.left,
                            top : that.options.co.top
                        }).addClass(that.options.proxy);
                return helper;
            }
        } else {
            var helper = "original";
        }

        // Prepare containment option
        if (options.containment) {
            if (options.containment.left != undefined
                    || options.containment.constructor == Array)
                return;
            if (options.containment == 'parent')
                options.containment = this.element.parentNode;
            if (options.containment == 'document') {
                options.containment = [
                        0,
                        0,
                        jQuery(document).width(),
                        (jQuery(document).height() || document.body.parentNode.scrollHeight)];
            } else { // I'm a node, so compute top/left/right/bottom
                var ce = jQuery(options.containment)[0];
                var co = jQuery(options.containment).offset({
                            border : false
                        });
                options.containment = [co.left, co.top,
                        co.left + (ce.offsetWidth || ce.scrollWidth),
                        co.top + (ce.offsetHeight || ce.scrollHeight)];
            }
        }

        // Destructive mode wraps the original element
        if (el.nodeName.match(/textarea|input|select|button|img/i))
            options.destructive = true;
        if (options.destructive) {

            jQuery(el)
                    .wrap('<div class="ui-wrapper"  style="position: relative; width: '
                            + jQuery(el).outerWidth()
                            + 'px; height: '
                            + jQuery(el).outerHeight() + ';"></div>');
            var oel = el;
            el = el.parentNode;
            this.element = el;

            // Move margins to the wrapper
            jQuery(el).css({
                        marginLeft : jQuery(oel).css("marginLeft"),
                        marginTop : jQuery(oel).css("marginTop"),
                        marginRight : jQuery(oel).css("marginRight"),
                        marginBottom : jQuery(oel).css("marginBottom")
                    });
            jQuery(oel).css({
                        marginLeft : 0,
                        marginTop : 0,
                        marginRight : 0,
                        marginBottom : 0
                    });

            o.proportionallyResize = o.proportionallyResize || [];
            o.proportionallyResize.push(oel);

            var b = [parseInt(jQuery(oel).css('borderTopWidth')),
                    parseInt(jQuery(oel).css('borderRightWidth')),
                    parseInt(jQuery(oel).css('borderBottomWidth')),
                    parseInt(jQuery(oel).css('borderLeftWidth'))];

        } else {
            var b = [0, 0, 0, 0];
        }

        if (options.destructive || !jQuery(".ui-resizable-handle", el).length) {
            // Adding handles (disabled not so common ones)
            var t = function(a, b) {
                jQuery(el).append("<div class='ui-resizable-" + a
                        + " ui-resizable-handle' style='" + b + "'></div>");
            };
            // t('n','top: '+b[0]+'px;');
            t('e',
                    'right: '
                            + b[1]
                            + 'px;'
                            + (options.zIndex ? 'z-index: ' + options.zIndex
                                    + ';' : ''));
            t('s',
                    'bottom: '
                            + b[1]
                            + 'px;'
                            + (options.zIndex ? 'z-index: ' + options.zIndex
                                    + ';' : ''));
            // t('w','left: '+b[3]+'px;');
            t('se',
                    'bottom: '
                            + b[2]
                            + 'px; right: '
                            + b[1]
                            + 'px;'
                            + (options.zIndex ? 'z-index: ' + options.zIndex
                                    + ';' : ''));
            // t('sw','bottom: '+b[2]+'px; left: '+b[3]+'px;');
            // t('ne','top: '+b[0]+'px; right: '+b[1]+'px;');
            // t('nw','top: '+b[0]+'px; left: '+b[3]+'px;');
        }

        // If other elements should be modified, we have to copy that array
        options.modifyThese = [];
        if (o.proportionallyResize) {
            options.proportionallyResize = o.proportionallyResize.slice(0);
            var propRes = options.proportionallyResize;

            for (var i in propRes) {
                if (propRes[i].constructor == String)
                    propRes[i] = jQuery(propRes[i], el);

                if (!jQuery(propRes[i]).length)
                    continue;

                var x = jQuery(propRes[i]).width() - jQuery(el).width();
                var y = jQuery(propRes[i]).height() - jQuery(el).height();
                options.modifyThese.push([jQuery(propRes[i]), x, y]);
            }

        }

        // Deal with the handles
        options.handles = {};
        if (!o.handles)
            o.handles = {
                n : '.ui-resizable-n',
                e : '.ui-resizable-e',
                s : '.ui-resizable-s',
                w : '.ui-resizable-w',
                se : '.ui-resizable-se',
                sw : '.ui-resizable-sw',
                ne : '.ui-resizable-ne',
                nw : '.ui-resizable-nw'
            };
        for (var i in o.handles) {
            options.handles[i] = o.handles[i];
        } // Copying the object
        for (var i in options.handles) {

            if (options.handles[i].constructor == String)
                options.handles[i] = jQuery(options.handles[i], el);

            if (!jQuery(options.handles[i]).length)
                continue;

            jQuery(options.handles[i]).bind('mousedown', function(e) {
                        self.interaction.options.axis = this.resizeAxis;
                    })[0].resizeAxis = i;

        }

        // If we want to auto hide the elements
        if (o.autohide)
            jQuery(this.element).addClass("ui-resizable-autohide").hover(
                    function() {
                        jQuery(this).removeClass("ui-resizable-autohide");
                    }, function() {
                        if (self.interaction.options.autohide
                                && !self.interaction.init)
                            jQuery(this).addClass("ui-resizable-autohide");
                    });

        // If we want to conserve the aspect ratio, we store the original aspect
        // ratio in the configuration
        if (o.aspectRatio
                && (o.aspectRatio == 'preserve' || o.aspectRatio === true))
            options.aspectRatio = jQuery(this.element).width()
                    / jQuery(this.element).height();

        jQuery.extend(options, {
                    helper : helper,
                    nonDestructive : true,
                    dragPrevention : 'input,button,select',
                    minHeight : options.minHeight || 50,
                    minWidth : options.minWidth || 150,
                    aspectRatio : options.aspectRatio || false,
                    startCondition : function(e) {
                        if (self.disabled)
                            return false;
                        for (var i in options.handles) {
                            if (jQuery(options.handles[i])[0] == e.target)
                                return true;
                        }
                        return false;
                    },
                    _start : function(h, p, c, t, e) {
                        self.start.apply(t, [self, e]); // Trigger the start
                        // callback
                    },
                    _beforeStop : function(h, p, c, t, e) {
                        self.stop.apply(t, [self, e]); // Trigger the stop
                        // callback
                    },
                    _drag : function(h, p, c, t, e) {
                        self.drag.apply(t, [self, e]); // Trigger the start
                        // callback
                    }
                });

        // Initialize mouse interaction
        this.interaction = new jQuery.ui.mouseInteraction(el, options);

        // Add the class for themeing
        jQuery(this.element).addClass("ui-resizable");

    }

    jQuery.extend(jQuery.ui.resizable.prototype, {
        plugins : {},
        prepareCallbackObj : function(self) {
            return {
                helper : self.helper,
                resizable : self,
                axis : self.options.axis,
                options : self.options
            }
        },
        destroy : function() {
            jQuery(this.element).removeClass("ui-resizable")
                    .removeClass("ui-resizable-disabled");
            this.interaction.destroy();
        },
        enable : function() {
            jQuery(this.element).removeClass("ui-resizable-disabled");
            this.disabled = false;
        },
        disable : function() {
            jQuery(this.element).addClass("ui-resizable-disabled");
            this.disabled = true;
        },
        start : function(that, e) {
            this.options.originalSize = [jQuery(this.element).width(),
                    jQuery(this.element).height()];
            this.options.originalPosition = jQuery(this.element)
                    .css("position");
            this.options.originalPositionValues = jQuery(this.element)
                    .position();

            if (this.options.modifyThese.length == 0
                    || !this.options.modifyThese[this.options.modifyThese.length
                            - 1][0].is('.ui-resizable'))
                this.options.modifyThese.push([jQuery(this.helper), 0, 0]);

            jQuery(that.element).triggerHandler("resizestart",
                    [e, that.prepareCallbackObj(this)], this.options.start);
            return false;
        },
        stop : function(that, e) {

            var o = this.options;
            jQuery(that.element).triggerHandler("resizestop",
                    [e, that.prepareCallbackObj(this)], this.options.stop);

            if (o.proxy) {
                jQuery(this.element).css({
                            width : jQuery(this.helper).width(),
                            height : jQuery(this.helper).height()
                        });
                if (o.originalPosition == "absolute"
                        || o.originalPosition == "fixed")
                    jQuery(this.element).css({
                                top : jQuery(this.helper).css("top"),
                                left : jQuery(this.helper).css("left")
                            });

            }

            return false;
        },
        drag : function(that, e) {

            // Prepare shortcuts and position
            var o = this.options;
            var rel = (o.originalPosition != "absolute" && o.originalPosition != "fixed");
            var co = rel ? o.co : this.options.originalPositionValues;
            var p = o.originalSize;
            this.pos = rel
                    ? [this.rpos[0] - o.cursorAt.left,
                            this.rpos[1] - o.cursorAt.top]
                    : [this.pos[0] - o.cursorAt.left,
                            this.pos[1] - o.cursorAt.top];

            // Calculate a rough new position
            var nw = p[0] + (this.pos[0] - co.left);
            var nh = p[1] + (this.pos[1] - co.top);

            // Conserve aspect ratio
            if (e.shiftKey && !o.aspectRatio)
                o.aspectRatio = p[0] / p[1];

            if (o.axis) {
                switch (o.axis) {
                    case 'e' :
                        nh = p[1];
                        break;
                    case 's' :
                        nw = p[0];
                        break;
                    case 'n' :
                    case 'ne' :

                        if (!o.proxy
                                && (o.originalPosition != "absolute" && o.originalPosition != "fixed"))
                            return false;

                        if (o.axis == 'n')
                            nw = p[0];
                        var mod = (this.pos[1] - co.top);
                        nh = nh - (mod * 2);
                        mod = nh <= o.minHeight
                                ? p[1] - o.minHeight
                                : (nh >= o.maxHeight
                                        ? 0 - (o.maxHeight - p[1])
                                        : mod);

                        if (o.containment
                                && co.top + mod < o.containment[1] - o.po.top) {
                            mod = (o.containment[1] - o.po.top) - co.top;
                            nh = nh + this.pos[1]
                                    - (o.containment[1] - o.po.top);
                        }

                        jQuery(this.helper).css('top', co.top + mod);
                        break;

                    case 'w' :
                    case 'sw' :

                        if (!o.proxy
                                && (o.originalPosition != "absolute" && o.originalPosition != "fixed"))
                            return false;

                        if (o.axis == 'w')
                            nh = p[1];
                        var mod = (this.pos[0] - co.left);
                        nw = nw - (mod * 2);
                        mod = nw <= o.minWidth
                                ? p[0] - o.minWidth
                                : (nw >= o.maxWidth
                                        ? 0 - (o.maxWidth - p[0])
                                        : mod);

                        if (o.containment
                                && co.left + mod < o.containment[0] - o.po.left) {
                            mod = (o.containment[0] - o.po.left) - co.left;
                            nw = nw + this.pos[0]
                                    - (o.containment[0] - o.po.left);
                        }

                        jQuery(this.helper).css('left', co.left + mod);
                        break;

                    case 'nw' :

                        if (!o.proxy
                                && (o.originalPosition != "absolute" && o.originalPosition != "fixed"))
                            return false;

                        var modx = (this.pos[0] - co.left);
                        nw = nw - (modx * 2);
                        modx = nw <= o.minWidth
                                ? p[0] - o.minWidth
                                : (nw >= o.maxWidth
                                        ? 0 - (o.maxWidth - p[0])
                                        : modx);

                        var mody = (this.pos[1] - co.top);
                        nh = nh - (mody * 2);
                        mody = nh <= o.minHeight
                                ? p[1] - o.minHeight
                                : (nh >= o.maxHeight
                                        ? 0 - (o.maxHeight - p[1])
                                        : mody);

                        if (o.containment
                                && co.top + mody < o.containment[1] - o.po.top) {
                            mody = (o.containment[1] - o.po.top) - co.top;
                            nh = nh + this.pos[1]
                                    - (o.containment[1] - o.po.top);
                        }
                        if (o.containment
                                && co.left + modx < o.containment[0]
                                        - o.po.left) {
                            modx = (o.containment[0] - o.po.left) - co.left;
                            nw = nw + this.pos[0]
                                    - (o.containment[0] - o.po.left);
                        }

                        jQuery(this.helper).css({
                                    left : co.left + modx,
                                    top : co.top + mody
                                });
                        break;
                }
            }

            if (e.shiftKey)
                nh = nw * (1 / o.aspectRatio);

            if (o.minWidth)
                nw = nw <= o.minWidth ? o.minWidth : nw;
            if (o.minHeight)
                nh = nh <= o.minHeight ? o.minHeight : nh;

            if (o.maxWidth)
                nw = nw >= o.maxWidth ? o.maxWidth : nw;
            if (o.maxHeight)
                nh = nh >= o.maxHeight ? o.maxHeight : nh;

            if (e.shiftKey)
                nh = nw * (1 / o.aspectRatio);

            var modifier = jQuery(that.element).triggerHandler("resize",
                    [e, that.prepareCallbackObj(this)], o.resize);
            if (!modifier)
                modifier = {};

            var left_handle_pos = co.left < this.pos[0] ? co.left : this.pos[0];
            var top_handle_pos = co.top < this.pos[1] ? co.top : this.pos[1];
            if (o.containment
                    && left_handle_pos + nw > o.containment[2] - o.po.left)
                nw = (o.containment[2] - o.po.left) - left_handle_pos;
            if (o.containment
                    && top_handle_pos + nh > o.containment[3] - o.po.top)
                nh = (o.containment[3] - o.po.top) - top_handle_pos;

            for (var i in this.options.modifyThese) {
                var c = this.options.modifyThese[i];
                c[0].css({
                            width : modifier.width ? modifier.width + c[1] : nw
                                    + c[1],
                            height : modifier.height
                                    ? modifier.height + c[2]
                                    : nh + c[2]
                        });
            }
            return false;

        }
    });

})(jQuery);
